#!/usr/bin/env python
"""
Acorn-SDR (A Collaboration of Radio Nodes)
(c) Bob Cowdery (G3UKB) 2009

File: dsp_service.py
Date: January 2009

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

The author can be reached by email at

bob@g3ukb.co.uk

================================================================================
Module Description:

The interface to HPSDR Ozy.

"""

#-------------------------------------------------------------------------------
# System imports
import sys
import os
from copy import deepcopy
from time import sleep
import Ice, IceStorm
import libozydrv as usb

# Application imports
import utils

#===============================================================================
class Ozy:
    
    #---------------------------------------------------------------------------
    def __init__(self, persist, rx_pub, tx_pub, ref, src, config, mic, frames, rate):
        # Stash the event service
        self.rx_pub = rx_pub
        self.tx_pub = tx_pub
        self.frames = frames
        self.rate = rate
        self.ref = ref
        self.src = src
        self.config = config
        self.mic = mic
        self.updates = {}
        
        # Get the Ozy device
        self.get_target(65534, 7)
        # Initialise
        usb.setFrames(int(frames))
        usb.setRate(int(rate))
        usb.setClockSrc1(self.ref)
        usb.setClockSrc2(self.src)
        usb.setBoardConfig(self.config)
        usb.setMicConnection(self.mic)

        self.freq = 0.0
        self.run = False
        self.first = True
        
        # Capabilities
        self.cap_low_freq = utils.persist_get(persist, 'Capability', 'lowFreq')
        self.cap_high_freq = utils.persist_get(persist, 'Capability', 'highFreq')
        
    #===========================================================================
    # Command updates
    #---------------------------------------------------------------------------    
    def power_on(self):
        # Use for threaded to start thread
        self.handle.startReader(6)
        self.run = True
        
    #---------------------------------------------------------------------------   
    def power_off(self):
        self.run = False
        # Use for threaded to stop thread
        self.handle.stopReader()
        
    #---------------------------------------------------------------------------   
    def freq_set(self, freq):
        self.freq = freq
        usb.setFreq(freq)
        self.rx_pub.freqUpdate(freq)
        
    #---------------------------------------------------------------------------   
    def freq_inc(self, inc):
        new_freq = self.freq + inc
        #print new_freq, self.cap_low_freq, self.cap_high_freq
        if new_freq >= self.cap_low_freq and new_freq <= self.cap_high_freq:
            self.freq = self.freq + inc
            usb.setFreq(self.freq)
            self.rx_pub.freqUpdate(self.freq)
    
    #---------------------------------------------------------------------------
    def mox(self, state):
        self.mox = state
        usb.setMox(state)
    
    # Careful with these else the audio will get messed up    
    #---------------------------------------------------------------------------   
    def set_audiorate(self, rate):
        self.updates['audio_rate'] = (rate, self.set_audiorate_1, True)
 
    #-----------------------------------------------------------------------
    def set_audiorate_1(self, value):
        self.rate = value
        
    #---------------------------------------------------------------------------   
    def set_audioframes(self, rate):
        self.updates['audio_frames'] = (rate, self.set_audioframes_1, True)
    
    #-----------------------------------------------------------------------
    def set_audioframes_1(self, value):
        self.frames = value
        
    #-----------------------------------------------------------------------
    def apply_updates(self):
        # This must be called at the end of a cycle so we don't apply updates
        # when the driver is running
        # ToDo: We should have a mutex around here as there is a window
        for key in self.updates.keys():
            self.updates[key][1](self.updates[key][0])
            if self.updates[key][2]:
                # Reinitialise the driver with the new rates
                was_running = False
                if self.run:
                    was_running = True
                    self.power_off()
                self.handle.flushBuffers()
                usb.setFrames(int(self.frames))
                usb.setRate(int(self.rate))
                if was_running:
                    self.power_on()
        self.updates.clear()
        
    #===========================================================================
    # Streaming
    #---------------------------------------------------------------------------
    def capture(self):
        if self.run:
            # Read a block from the device
            samples =  self.handle.readSamples(6)
            return samples
    
    #---------------------------------------------------------------------------
    def write(self):
        if self.run:
            self.handle.writeOzy(2)
            
    #---------------------------------------------------------------------------
    def output(self, samples):
        if self.run:
            # Adjust the frame rate so its always 48k worth of buffer
            # ToDo find a better way to do this
            if self.rate == 96000:
                frames_to_write = self.frames/2
            elif self.rate == 192000:
                frames_to_write = self.frames/4
            else:
                frames_to_write = self.frames
            
            # We might have just changed buffer size and got a residual buffer
            if int(frames_to_write) <>  len(samples[0]):
                #print 'Ozy Service: (Received frame of ', len(samples[0]), ', expected ', frames_to_write, '. Ignore if you did a live change)'
                return
            #print 'Frames: ', int(frames_to_write), 'Samples: ', samples[0][:20]
            self.handle.writeSamples(2, int(frames_to_write), samples[0], samples[1])

    #===========================================================================
    # USB
    #---------------------------------------------------------------------------
    #---------------------------------------------------------------------------
    def get_target(self, vendor_id, product_id):
        
        try:
            self.get_device(vendor_id, product_id)
            #print 'Got device', self.dev
            self.open()
            #print 'Opened device'
            #self.dev.handle.reset()
        except:
            print 'Sorry, the Ozy USB device could not be found'
            
    
    #---------------------------------------------------------------------------
    def get_device(self, vendor_id, product_id):
        
        busses = usb.busses()
        self.handle = None
        count = 0
        for bus in busses:
            devices = bus.devices
            for dev in devices:
                if dev.idVendor==vendor_id and dev.idProduct==product_id:
                    #print 'Found device'
                    self.dev = dev
                    self.conf = self.dev.configurations[0]
                    #print 'Got configuration [0]', self.conf.value
                    self.intf = self.conf.interfaces[0][0]
                    #print 'Got interface [0][0]', self.intf.interfaceNumber
                    self.endpoints = []
                    for endpoint in self.intf.endpoints:
                        #print 'Endpoint: ', endpoint
                        self.endpoints.append(endpoint)

    #---------------------------------------------------------------------------
    def open(self):
        if self.handle:
            self.handle = None
        
        try:
            self.handle = self.dev.open()
            #print 'Device handle: ', self.handle
            # Is this important"!!
            #self.handle.detachKernelDriver(0)
            #print 'Detached Kernel Driver 0'
            #self.handle.detachKernelDriver(1)
            #print 'Detached Kernel Driver 1'
            self.handle.setConfiguration(self.conf)
            #print 'Set Configuration'
            self.handle.claimInterface(self.intf)
            #print 'Claimed Interface'
            self.handle.setAltInterface(self.intf)
            #print 'Set alternate interface'
        except:
             print "Unexpected error on open:", sys.exc_info()
             raise